// standard platform header
#include <core/common/platform.h>

// headers
#include <core/common/global.h>
#include <core/common/log.h>
#include <core/common/file.h>
#include <core/common/string.h>
#include <core/common/system.h>
#include <core/common/limits.h>
#include <core/common/status.h>
#include <core/common/module.h>
#include <core/common/configure.h>

//
// class Common::ModuleConfig
//
core::common::ModuleConfigure::ModuleConfigure()
{
	enabled_ = true;
}

core::common::ModuleConfigure::~ModuleConfigure()
{
}

bool core::common::ModuleConfigure::load( const json::Value &node )
{
	if( node.isNull() )
	{
		//__ULOG_STDERR(__ULOG_FMT("ModuleConfigure", "Node is null"));
		return false;
	}

	if( node.isMember("enabled") ) enabled_ = node["enabled"].asBool();
	if( node.isMember("name") ) name_ = node["name"].asString();

	return true;
}

//
// class Common::LogConfigure
//

core::common::LogConfigure::LogConfigure()
{
	type_  = core::common::Log::kLogTypeStdout | core::common::Log::kLogTypeFile;
	level_ = core::common::Log::kLogLevelAll;
	append_ = false;
	timeCapacity_ = 30 * 24 * 3600;
	sizeCapacity_ = 100 * core::common::kByteUnitsPerMega;
}

core::common::LogConfigure::~LogConfigure()
{
}

bool core::common::LogConfigure::load( const json::Value &node )
{
	if( !ModuleConfigure::load(node) )
	{
		return false;
	}

	core::common::StringList types, levels;

	if( node.isMember("type") ) typeString_ = node["type"].asString();

	if( node.isMember("level") ) typeString_ = node["level"].asString();
	if( node.isMember("append") ) append_ = node["append"].asBool();
	if( node.isMember("file") ) file_ = node["file"].asString();
	if( node.isMember("timeCapacity") ) timeCapacity_ = node["timeCapacity"].asUInt() * 3600 * 24;
	if( node.isMember("sizeCapacity") ) sizeCapacity_ = node["sizeCapacity"].asUInt64() * core::common::kByteUnitsPerMega;
	if( node.isMember("timeMilliSeconds") ) timeMilliSeconds_ = node["timeMilliSeconds"].asBool();

	// normalize file path
	core::common::File::normalizePath(file_);

	// parse type string
	type_ = core::common::Log::kLogTypeNone;
	core::common::String::split(typeString_, types, '|');

	for( core::common::StringList::iterator itr = types.begin(); itr != types.end(); itr ++ )
	{
		std::string strTypeItem = core::common::String::toUpper(*itr);

		if( strTypeItem == "ALL" )         type_  = core::common::Log::kLogTypeAll;
		else if( strTypeItem == "STDOUT" ) type_ |= core::common::Log::kLogTypeStdout;
		else if( strTypeItem == "STDERR" ) type_ |= core::common::Log::kLogTypeStderr;
		else if( strTypeItem == "FILE" )   type_ |= core::common::Log::kLogTypeFile;
	}

	// parse level string
	level_ = core::common::Log::kLogLevelNone;
	core::common::String::split(levelString_, levels, '|');

	for( core::common::StringList::iterator itr = levels.begin(); itr != levels.end(); itr ++ )
	{
		std::string levelItem = core::common::String::toUpper(*itr);

		if( levelItem == "ALL" )          level_  = core::common::Log::kLogLevelAll;
		else if( levelItem == "TRACE" )   level_ |= core::common::Log::kLogLevelTrace;
		else if( levelItem == "INFO" )    level_ |= core::common::Log::kLogLevelInfo;
		else if( levelItem == "WARNING" ) level_ |= core::common::Log::kLogLevelWarning;
		else if( levelItem == "ERROR" )   level_ |= core::common::Log::kLogLevelError;
		else if( levelItem == "FATAL" )   level_ |= core::common::Log::kLogLevelFatal;
	}

	return true;
}

std::string core::common::LogConfigure::getTypeName()
{
	std::string type;

	if( type_ & core::common::Log::kLogTypeStdout ) core::common::String::appendFormat(type, "%s", "STDOUT|");
	if( type_ & core::common::Log::kLogTypeStderr ) core::common::String::appendFormat(type, "%s", "STDERR|");
	if( type_ & core::common::Log::kLogTypeFile )   core::common::String::appendFormat(type, "%s", "FILE|");

	if( type.size() > 0 && type[type.size() - 1] == '|' )
	{
		type.erase(type.size() - 1, 1);
	}

	return type;
}

std::string core::common::LogConfigure::getLevelName()
{
	std::string level;

	if( level_ & core::common::Log::kLogLevelTrace )   core::common::String::appendFormat(level, "%s", "TRACE|");
	if( level_ & core::common::Log::kLogLevelInfo )    core::common::String::appendFormat(level, "%s", "INFO|");
	if( level_ & core::common::Log::kLogLevelWarning ) core::common::String::appendFormat(level, "%s", "WARNING|");
	if( level_ & core::common::Log::kLogLevelError )   core::common::String::appendFormat(level, "%s", "ERROR|");
	if( level_ & core::common::Log::kLogLevelFatal )   core::common::String::appendFormat(level, "%s", "FATAL|");

	if( level.size() > 0 && level[level.size() - 1] == '|' )
	{
		level.erase(level.size() - 1, 1);
	}

	return level;
}

//
// class Common::ConfigureBase
//

core::common::Configure::Configure()
{
	heartbeatInterval_ = core::common::kStatusCheckSleepTime;
}

core::common::Configure::~Configure()
{
}

bool core::common::Configure::loadBuffer( const std::string &buffer, void *pParam )
{
	std::string moduleDirectory = core::common::File::getModuleDirectory(pParam);
	size_t separatorPos = moduleDirectory.rfind(core::common::File::kPathSeparator);
	if( separatorPos != std::string::npos )
	{
		path_ = moduleDirectory.substr(0, separatorPos);
	}
	else
	{
		path_ = core::common::String::format("%s/..", moduleDirectory.c_str());
	}

	if( !document_.fromString(buffer, false) )
	{
		__ULOG_STDERR(__ULOG_FMT("Configure", "Load json buffer(size: "_SIZE_TFMT_") failed"), buffer.size());
		return false;
	}

	return loadDocument();
}

bool core::common::Configure::loadDocument()
{
	json::Value &globalNode = document_["global"];
	if( globalNode.isMember("shareName") ) shareName_ = globalNode["shareName"].asString();
	if( globalNode.isMember("notifierName") ) notifierName_ = globalNode["notifierName"].asString();
	if( globalNode.isMember("heartbeatInterval") ) heartbeatInterval_ = globalNode["heartbeatInterval"].asUInt();

	if( !log_.load(document_["log"]) )
	{
		__ULOG_STDERR(__ULOG_FMT("Configure", "Load log information failed"));
		return false;
	}

	if( !core::common::File::isAbsolutePath(log_.file_) )
	{
		log_.file_ = core::common::String::format("%s/%s", path_.c_str(), log_.file_.c_str());
		core::common::File::normalizePath(log_.file_);
	}

	return true;
}

bool core::common::Configure::load( const std::string &file, void *param )
{
	std::string modulePath = core::common::File::getModuleDirectory(param);
	size_t separatorPos = modulePath.rfind(core::common::File::kPathSeparator);
	if( separatorPos != std::string::npos )
	{
		path_ = modulePath.substr(0, separatorPos);
	}
	else
	{
		path_ = core::common::String::format("%s/..", modulePath.c_str());
	}

	file_ = file;
	if( !core::common::File::isAbsolutePath(file_) )
	{
		file_ = core::common::String::format("%s/%s", path_.c_str(), file.c_str());
	}
	core::common::File::normalizePath(path_);
	core::common::File::normalizePath(file_);

	std::string buffer;
	core::common::File reader;
	if( !reader.open(file_, core::common::File::kModeRead | core::common::File::kModeBinary) ||
		!reader.readData(buffer, 102400) ||
		!document_.fromString(file_, false) )
	{
		__ULOG_STDERR(__ULOG_FMT_ERR("Configure", "Load json file(%s) failed"), file.c_str(), __UERR_CODE, __UERR_STR);
		return false;
	}

	return loadDocument();
}

void core::common::Configure::traceSummary()
{
	// 
	// global
	//

	__ULOG_INFO(__ULOG_FMT("Configure", "Working Directory: %s"), path_.c_str());
	__ULOG_INFO(__ULOG_FMT("Configure", "Source File: %s"), file_.c_str());
	__ULOG_INFO(__ULOG_FMT("Configure", "Share Name: %s"), shareName_.c_str());
	__ULOG_INFO(__ULOG_FMT("Configure", "Notifier Name: %s"), notifierName_.c_str());
	__ULOG_INFO(__ULOG_FMT("Configure", "Heartbeat Interval: %u ms"), heartbeatInterval_);
	__ULOG_INFO(__ULOG_FMT("Configure", "Local Timezone: %d sec(s)"), (int)core::common::getLocalTimezoneSeconds());
	__ULOG_INFO(__ULOG_FMT("Configure", "--------------------- SEPARATOR -----------------------"));

	//
	// log
	//

	__ULOG_INFO(__ULOG_FMT("Configure", "[Log]: %s"), log_.name_.c_str());
	__ULOG_INFO(__ULOG_FMT("Configure", "Output Type: %s"), log_.getTypeName().c_str());
	__ULOG_INFO(__ULOG_FMT("Configure", "Output Level: %s"), log_.getLevelName().c_str());
	__ULOG_INFO(__ULOG_FMT("Configure", "Output File: %s"), log_.file_.c_str());
	__ULOG_INFO(__ULOG_FMT("Configure", "Append File: %s"), log_.append_ ? "true" : "false");
	__ULOG_INFO(__ULOG_FMT("Configure", "Size Capacity: "_U64FMT_" MB"), log_.sizeCapacity_ / 1024 / 1024);
	__ULOG_INFO(__ULOG_FMT("Configure", "Time Capacity: %u day(s)"), log_.timeCapacity_ / 24 / 3600);
	__ULOG_INFO(__ULOG_FMT("Configure", "Time Milli-Seconds: %s"), log_.timeMilliSeconds_ ? "true" : "false");
	__ULOG_INFO(__ULOG_FMT("Configure", "--------------------- SEPARATOR -----------------------"));
}
